#include "stdafx.h"
#include "Game.h"

#include "DrawableGameComponent.h"
#include "ContentManager.h"
#include "Vertex.h"

#include <iostream>

namespace Framework {

using namespace std;

Game::Game(void) {
    this->components = new GameComponentCollection();
    this->contentManager = new ContentManager(this);
    this->gameTime = 0;
    this->isDrawing = false;
    this->soundDevice = new SoundDevice();
    this->vertexDecleration = NULL;
}

Game::~Game(void) {
    d3d::Delete(this->components);
    d3d::Delete(this->contentManager);
    d3d::Delete(this->soundDevice);
}

Camera* Game::GetCamera() {
    return this->camera;
}

void Game::SetCamera(Camera* val) {
    this->camera = val;
}

GameComponentCollection* Game::GetComponents() {
    return this->components;
}

ContentManager* Game::GetContentManager() const {
    return this->contentManager;
}

string Game::GetWorkingDirectory() {
    TCHAR NPath[MAX_PATH];
    GetCurrentDirectory(MAX_PATH, NPath);
    return NPath;
}

string Game::GetExePath() {
    char buffer[MAX_PATH];
    GetModuleFileName(NULL, buffer, MAX_PATH);
    string::size_type pos = string(buffer).find_last_of("\\/");
    return string(buffer).substr(0, pos);
}

FLOAT Game::GetGameTime() const {
    return this->gameTime;
}

IDirect3DDevice9* Game::GetGraphicsDevice() {
    return this->graphicsDevice;
}

SoundDevice* Game::GetSoundDevice() const {
    return this->soundDevice;
}

void Game::BeginDraw() {
    if (this->isDrawing) {
        return;
    }

    this->isDrawing = true;
    this->GetGraphicsDevice()->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0x00000000, 1.0f, 0);
    this->GetGraphicsDevice()->BeginScene();
}

void Game::Draw(FLOAT timeDelta) {
    for (int i = 0; i < this->components->GetCount(); i++) {
        GameComponent* gameComponent = this->components->GetItem(i);
        DrawableGameComponent* drawable = dynamic_cast<DrawableGameComponent*>(gameComponent);
        if (drawable != NULL && drawable->GetVisible()) {
            drawable->Draw(timeDelta);
        }
    }
}

void Game::EndDraw() {
    if (!this->isDrawing) {
        return;
    }

    this->isDrawing = false;
    this->GetGraphicsDevice()->EndScene();
    this->GetGraphicsDevice()->Present(0, 0, 0, 0);
}

void Game::Finalize() {
    this->UnloadContent();
}

void Game::Initialize() {
    for (int i = 0; i < this->components->GetCount(); i++) {
        this->components->GetItem(i)->Initialize();
    }

    this->LoadContent();
}

void Game::LoadContent() {
    for (int i = 0; i < this->components->GetCount(); i++) {
        GameComponent* gameComponent = this->components->GetItem(i);
        DrawableGameComponent* drawable = dynamic_cast<DrawableGameComponent*>(gameComponent);
        if (drawable != NULL) {
            drawable->LoadContent();
        }
    }
}

void Game::Run(IDirect3DDevice9* graphicsDevice) {
    this->graphicsDevice = graphicsDevice;

    this->camera = new Camera(this->graphicsDevice);
    this->camera->SetPosition(D3DXVECTOR3(0, 0, 0));
    this->graphicsDevice->SetTransform(D3DTS_VIEW, &camera->GetViewMatrix());

    D3DVIEWPORT9 viewport;
    this->graphicsDevice->GetViewport(&viewport);

    D3DXMATRIX projection;
    D3DXMatrixPerspectiveFovLH(
        &projection,
        D3DX_PI * 0.41666f,
        (float)viewport.Width / (float)viewport.Height,
        0.1f,
        10000.0f);
    this->camera->SetProjectionMatrix(projection);
    this->graphicsDevice->SetTransform(D3DTS_PROJECTION, &projection);

    D3DDEVICE_CREATION_PARAMETERS creationParam;
    this->graphicsDevice->GetCreationParameters(&creationParam);
    this->soundDevice->Initialize(creationParam.hFocusWindow);

    this->graphicsDevice->CreateVertexDeclaration(Vertex::GetVertexElement(), &this->vertexDecleration);
    this->graphicsDevice->SetVertexDeclaration(this->vertexDecleration);
    this->Initialize();
}

void Game::UnloadContent() {
    for (int i = 0; i < this->components->GetCount(); i++) {
        GameComponent* gameComponent = this->components->GetItem(i);
        DrawableGameComponent* drawable = dynamic_cast<DrawableGameComponent*>(gameComponent);
        if (drawable != NULL) {
            drawable->UnloadContent();
        }
    }
}

void Game::Update(FLOAT timeDelta) {
    this->gameTime += timeDelta;
    for (int i = 0; i < this->components->GetCount(); i++) {
        GameComponent* component = this->components->GetItem(i);
        if (component->GetEnabled()) {
            components->GetItem(i)->Update(timeDelta);
        }
    }
}

}
